﻿namespace IBTrader.Modules.Tws
{
    using AxTWSLib;
    using IBTrader.Modules.External;
    using IBTrader.Modules.Market;
    using NLog;
    using System;
    using TWSLib;

    public class IB : AxTws, IIB
    {
        public new event AxTWSLib._DTwsEvents_historicalDataEventHandler historicalData;
        public event AxTWSLib._DTwsEvents_historicalDataEventHandler historicalDataEnd;
        public new event EventHandler<ContractDetailsEventArgs> contractDetails;
        public new event EventHandler<ExecDetailsEventArgs> execDetails;
        public new event EventHandler<UpdatePortfolioEventArgs> updatePortfolio;
        public event EventHandler<OpenOrderEventArgs> openOrder;
        public event EventHandler Connected;
        public int ClientId { get; private set; }
        public string AccountName { get; private set; }
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();
        public string Host { get; private set; }
        public int Port { get; private set; }
        public IB() { }
        public IB(string host, string port, string clientId, string accountName) : base()
        {
            Init(host, port, clientId, accountName);
        }
        public void Init(string host, string port, string clientId, string accountName = null)
        {
            this.Host = host;
            this.Port = int.Parse(port);
            this.ClientId = int.Parse(clientId);
            this.AccountName = accountName;

            base.historicalData += (o, e) =>
            {
                var handler = e.date.StartsWith("finished") ? historicalDataEnd : historicalData;
                if (handler != null) handler(o, e);
            };

            base.contractDetailsEx += (o, e) =>
                {
                    if (contractDetails != null) contractDetails(this, new ContractDetailsEventArgs
                    {
                        RequestId = e.reqId,
                        ContractDetails = e.contractDetails
                    });
                };

            base.execDetailsEx += (o, e) =>
                {
                    if (execDetails != null) execDetails(this, new ExecDetailsEventArgs
                    {
                        reqId=e.reqId,
                        contract=e.contract,
                        execution=e.execution
                    });
                };

            base.openOrderEx += (o, e) =>
            {
                if (openOrder != null) openOrder(this, new OpenOrderEventArgs
                {
                    orderId = e.orderId,
                    contract = e.contract,
                    order = e.order,
                    orderState = e.orderState
                });
            };
            base.updatePortfolioEx += (o, e) =>
            {
                if (updatePortfolio != null) updatePortfolio(this, new UpdatePortfolioEventArgs
                {
                    contract = e.contract,
                    position = e.position,
                    marketPrice = e.marketPrice,
                    marketValue = e.marketValue,
                    averageCost = e.averageCost,
                    unrealizedPNL = e.unrealizedPNL,
                    realizedPNL = e.realizedPNL,
                    accountName = e.accountName
                });
            };
        }
        public void Connect()
        {
            base.connect(Host, Port, ClientId);
            if (Connected != null) Connected(this, EventArgs.Empty);
        }
        public void Disconnect()
        {
            base.disconnect();
        }
        public bool IsConnected()
        {
            return !string.IsNullOrWhiteSpace(TwsConnectionTime);
        }

        public object CreateContract(string symbol, string securityType, string exchange, string currency, 
            string multiplier = null, string primaryExchange = null, DateTime? dt = null)
        {
            IContract contract = createContract();
            contract.symbol = symbol;
            contract.secType = securityType;
            contract.exchange = exchange;
            contract.currency = currency;
            contract.primaryExchange = primaryExchange;
            contract.multiplier = multiplier;
            if (securityType == "FUT")
            {
                contract.expiry = Expirations.Expiration(symbol, dt ?? DateTime.UtcNow);
                contract.includeExpired = 1;
            }
            //if (isISIN)
            //{
            //    contract.secIdType = "ISIN";
            //    contract.secId = symbol;
            //}
            //else
            //{
            //    contract.symbol = symbol;
            //}
            return contract;
        }

        public void reqHistoricalData(int tickerId, object contract, string endDateTime, string durationString, string barSizeSetting, string whatToShow, int useRTH, int formatDate)
        {
            base.reqHistoricalDataEx(tickerId, contract as IContract, endDateTime, durationString, barSizeSetting, whatToShow, useRTH, formatDate);
        }

        public void reqContractDetails(int reqId, object contract)
        {
            base.reqContractDetailsEx(reqId, contract as IContract);
        }

        public void reqMktData(int tickerId, object contract, string genericTickList, bool snapshot)
        {
            base.reqMktDataEx(tickerId, contract as IContract, genericTickList, snapshot ? 1 : 0);
        }

        public void reqNewsBulletins(bool allMessages)
        {
            base.reqNewsBulletins(allMessages ? 1 : 0);
        }

        public void reqRealTimeBars(int tickerId, object contract, int barSize, string whatToShow, bool useRTH)
        {
            base.reqRealTimeBarsEx(tickerId, contract as IContract, barSize, whatToShow, useRTH ? 1 : 0);
        }

        public void reqExecutions(int reqId, object filter)
        {
            base.reqExecutionsEx(reqId, filter as IExecutionFilter);
        }

        public void reqMktDepth(int tickerId, object contract, int numRows)
        {
            base.reqMktDepthEx(tickerId, contract as IContract, numRows);
        }

        public void reqFundamentalData(int reqId, object contract, string reportType)
        {
            base.reqFundamentalData(reqId, contract as IContract, reportType);
        }

        public void placeOrder(int id, object contract, object order)
        {
            base.placeOrderEx(id, contract as IContract, order as IOrder);
        }

        public object CreateExecutionFilter()
        {
            return createExecutionFilter();
        }
    
        public object CreateOrder()
        {
            return createOrder();
        }

        public object CreateOrder(OrderAction action, OrderType orderType, double lmtPrice, int quantity, string orderRef, TimeSpan orderDuration)
        {
            var order = createOrder();
            order.action = action.ToString();
            order.orderType = orderType == OrderType.MKTPROT ? "MKT PRT" : orderType.ToString(); // LMT, MKT, MKT PRT, STP, REL, TRAIL
            if (orderType == OrderType.LMT || orderType == OrderType.REL) order.lmtPrice = lmtPrice;
            order.totalQuantity = quantity;
            order.transmit = 1;
            order.orderRef = orderRef;
            order.timeInForce = "GTD"; // DAY, GTD, OPG, IOC
            if (order.timeInForce == "GTD") order.goodTillDate = (Time.EasternNow + Time.Difference + orderDuration).ToString("yyyyMMdd HH:mm:ss EST");
            order.clientId = ClientId;
            order.account = AccountName;
            return order;
        }
    }
}